﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WebCam.Data.WebCamWorker;
using WebCam.Data.WebCamWorker.OneShotWebcamCore;
using System.Drawing;
using System.ComponentModel;
using WebcamCore;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WebCam.Logic.Detectors
{
    public delegate void OutputImageFormed(Bitmap outputImage, List<int> combo);
    public delegate void MotionCaptureImageFormed(Bitmap outputImage, double leftAngle, double rightAngle);
    public delegate void WindowStart(string programName);
    public delegate void JediImageFormed(Bitmap outputImage);
    //public delegate void ColorsAdded(Color color);

    public class CoreController
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string windowName);

        [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
        public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindow(IntPtr hwndParent, string windowName);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        const int WM_LBUTTONDOWN = 0x0201;
        const int WM_LBUTTONUP = 0x0202;
        const int WM_MOVING = 0x0216;
        const short SWP_NOMOVE = 0X2;
        const short SWP_NOSIZE = 1;
        const short SWP_NOZORDER = 0X4;
        const int SWP_SHOWWINDOW = 0x0040;
        const int WM_KEYDOWN = 0x0100;
        const int WM_KEYUP = 0x0101;
        const int WM_CHAR = 0x0102;
        const int VK_SPACE = 0x20;

        SimpleWebCam webcam;
        
        // список контрольных точек
        List<ControlArea> areas;

        // список последовательно выделенных контрольных точек
        List<int> currentCombo = new List<int>();

        // интервал в миллисекундах между попаданиями в контрольные точки, при котором они засчитываются как последовательные
        int interval = 1000;

        // интервал в милличекундах между попаданиями в контрольные точки, при котором считается, что мы все еще находимся на одной точке
        int stayInterval = 200;

        // время последнего попадания в контрольную точку
        DateTime lastHit = DateTime.MinValue;

        List<int[]> combos = new List<int[]>();


        public CoreController()
        {
            //MotionCaptureWorker worker = new MotionCaptureWorker();
            areas = new List<ControlArea>();

            areas.Add(new ControlArea(id: 1, rect: new Rectangle(10, 10, 50, 50)));
            areas.Add(new ControlArea(id: 2, rect: new Rectangle(320 - 25, 10, 50, 50)));
            areas.Add(new ControlArea(id: 3, rect: new Rectangle(10, 480-60, 50, 50)));
            areas.Add(new ControlArea(id: 4, rect: new Rectangle(640 - 60, 10, 50, 50)));

            combos.Add(new int[] { 1, 2 });
            combos.Add(new int[] { 1, 3 });
            combos.Add(new int[] { 3, 1});
            combos.Add(new int[] { 1, 4 });
            combos.Add(new int[] { 4, 1 });


            SimpleControllerWorker worker = new SimpleControllerWorker(areas);
            worker.OnWorkerComplete += new WorkerComplete(worker_OnWorkerComplete);
            webcam = new SimpleWebCam(worker);

            //LightsaberDetector saberWorker = new LightsaberDetector();
            //saberWorker.OnWorkerComplete += new WorkerComplete(saberWorker_OnWorkerComplete);
            //webcam = new SimpleWebCam(saberWorker);
            
        }

        void saberWorker_OnWorkerComplete(object sender, WorkerComletedEventArgs args)
        {
            Bitmap bmp = args.Bitmap;
            var arguments = args as LightsaberEventArgs;
            if (arguments != null)
            {
                if (!(arguments.A == double.NaN || arguments.B == double.NaN || (arguments.A == 0 && arguments.B == 0)))
                {
                    Graphics g = Graphics.FromImage(bmp);
                    using (Pen p = new Pen(Color.Green, 40))
                    {
                        float x1 = arguments.StartX;
                        float y1 = arguments.StartY;

                        float D = 500;

                        double angle = Math.Atan(arguments.A);

                        float x2 = (float)(x1 - D * Math.Cos(angle));
                        float y2 = (float)(y1 - D * Math.Sin(angle));

                        //float y1 = arguments.StartY;//(float)(arguments.A * x1 + arguments.B);
                        //float y2 = (float)(arguments.A * x2 + arguments.B);

                        g.DrawLine(p, x1, y1, x2, y2);
                    }
                }

                if (OnJedyImageFormed != null)
                {
                    OnJedyImageFormed(bmp);
                }
            }
        }

        public void Start()
        {
            webcam.Start();
        }

        public void InitCompleted()
        {

        }

        public void Stop()
        {
            webcam.Stop();
        }

        public int Width { get { return webcam.Width; } }
        public int Height { get { return webcam.Height; } }

        public event OutputImageFormed OnOutputImageFormed;
        public event MotionCaptureImageFormed OnMotionCaptureImageFormed;
        public event JediImageFormed OnJedyImageFormed;


        void worker_OnWorkerComplete(object sender, WorkerComletedEventArgs args)
        {
            Bitmap bmp = args.Bitmap;

            var arguments = args as SimpleControllerEventArgs;

            if (arguments != null && arguments.Selected != null && arguments.Selected.Count != 0)
            {
                if (arguments.Selected.Count == 1)
                {
                    var pointState = CheckComboInterval(arguments.Selected[0]);
                    switch(pointState)
                    {
                        case HitTestResult.NewCombo:
                            currentCombo.Clear();
                            currentCombo.Add(arguments.Selected[0]);
                            break;
                        case HitTestResult.NewHit:
                            currentCombo.Add(arguments.Selected[0]);
                            int comboNumber = FindCombo();
                            if (comboNumber != -1)
                            {
                                switch(comboNumber)
                                {
                                    case 0:
                                        IntPtr wnd = FindWindow(IntPtr.Zero, "PowerPoint Slide Show - [" + "Смешанная реальность – это просто.pptx" + "]");
                                        SetForegroundWindow(wnd);
                                        SendKeys.SendWait(" ");
                                        currentCombo.Clear();
                                        lastHit = DateTime.MinValue;
                                        break;
                                    case 1:
                                        StartMotionCapture();
                                        currentCombo.Clear();
                                        lastHit = DateTime.MinValue;
                                        break;
                                    case 2:
                                        FinishMotionCapture();
                                        currentCombo.Clear();
                                        lastHit = DateTime.MinValue;
                                        break;
                                    case 3:
                                        StartLightSaber();
                                        currentCombo.Clear();
                                        lastHit = DateTime.MinValue;
                                        break;
                                    case 4:
                                        FinishLightSaber();
                                        currentCombo.Clear();
                                        lastHit = DateTime.MinValue;
                                        break;
                                    default: break;
                                }
                            }
                            break;                      
                    }
                    if (currentCombo.Count > 2)
                    {
                        currentCombo.Clear();
                        lastHit = DateTime.MinValue;
                    }
                    else
                    {
                        lastHit = DateTime.Now;
                    }
                }

                //if (arguments.Selected.HasFlag(ControlAreaTypes.PresentationStart))
                //{
                //    start = DateTime.Now;
                //}
                //if (arguments.Selected.HasFlag(ControlAreaTypes.PresentationFinish) && start != null)
                //{
                //    var finishTime = DateTime.Now;
                //    var betwen = finishTime.Subtract(start);
                //    if (betwen.TotalSeconds < 2)
                //    {
                //        IntPtr wnd = FindWindow(IntPtr.Zero, "PowerPoint Slide Show - [" + "Смешанная реальность – это просто.pptx" + "]");
                //        SetForegroundWindow(wnd);
                //        SendKeys.SendWait(" ");
                //    }
                //    start = DateTime.MinValue;
                //}
            }

            using (Pen p = new Pen(Color.Blue, 3))
            {
                foreach (var area in areas)
                {
                    var rect = area.Rect;
                    Graphics g = Graphics.FromImage(bmp);
                    g.DrawRectangle(p, area.Rect);
                }
            }

            if (OnOutputImageFormed != null)
            {
                OnOutputImageFormed(bmp, currentCombo);
            }
        }

        private void FinishLightSaber()
        {
            var worker = webcam.Workers.FirstOrDefault(p => p.GetType() == typeof(LightsaberDetector));
            if (worker != null)
            {
                worker.OnWorkerComplete -= new WorkerComplete(saberWorker_OnWorkerComplete);
                webcam.RemoveWorker(typeof(LightsaberDetector));
                if (OnWindowStartCommand != null)
                {
                    OnWindowStartCommand("KillLightsaber");
                }
            }
        }

        private void StartLightSaber()
        {
            if (OnWindowStartCommand != null)
            {
                OnWindowStartCommand("Lightsaber");
            }
            LightsaberDetector saberWorker = new LightsaberDetector();
            saberWorker.OnWorkerComplete += new WorkerComplete(saberWorker_OnWorkerComplete);
            webcam.AddWorker(saberWorker);
            
        }

        private void FinishMotionCapture()
        {
            var worker = webcam.Workers.FirstOrDefault(p => p.GetType() == typeof(MotionCaptureWorker));
            if (worker != null)
            {
                worker.OnWorkerComplete -= new WorkerComplete(mcworker_OnWorkerComplete);
                webcam.RemoveWorker(typeof(MotionCaptureWorker));
                if (OnWindowStartCommand != null)
                {
                    OnWindowStartCommand("KillButterfly");
                }
            }
        }

        public event WindowStart OnWindowStartCommand;

        private void StartMotionCapture()
        {
            if (OnWindowStartCommand != null)
            {
                OnWindowStartCommand("Butterfly");
            }

            MotionCaptureWorker mcworker = new MotionCaptureWorker();
            mcworker.OnWorkerComplete += new WorkerComplete(mcworker_OnWorkerComplete);
            webcam.AddWorker(mcworker);
        }

        void mcworker_OnWorkerComplete(object sender, WorkerComletedEventArgs args)
        {
            Bitmap bmp = args.Bitmap;
            var arguments = args as MotionCaptureEventArgs;

            if (arguments != null)
            {                
                Graphics g = Graphics.FromImage(bmp);
                g.DrawLine(Pens.Red, (float)bmp.Width / 2, (float)bmp.Height / 2, (float)(bmp.Width / 2 + 200 * Math.Sin(arguments.LeftAngle)), bmp.Height-(float)(bmp.Height / 2 + 200 * Math.Cos(arguments.LeftAngle)));
                g.DrawLine(Pens.Red, (float)bmp.Width / 2, (float)bmp.Height / 2, (float)(bmp.Width / 2 + 200 * Math.Sin(arguments.RightAngle)), bmp.Height-(float)(bmp.Height / 2 + 200 * Math.Cos(arguments.RightAngle)));
            }

            if (OnMotionCaptureImageFormed != null)
            {
                OnMotionCaptureImageFormed(bmp, arguments.LeftAngle, arguments.RightAngle);
            }
        }

        private int FindCombo()
        {
            for(int ci = 0 ; ci < combos.Count ; ci++)
            {
                if (combos[ci].Length == currentCombo.Count)
                {
                    bool isCombo = true;
                    for (int i = 0; i < combos[ci].Length; i++)
                    {
                        if (combos[ci][i] != currentCombo[i])
                        {
                            isCombo = false;
                            break;
                        }
                    }
                    if (isCombo)
                    {
                        return ci;
                    }
                }
            }
            return -1;
        }

        private enum HitTestResult
        {
            /// <summary>
            /// Ничего не менялось, мы на старом месте, не надо считать как новое выделение
            /// </summary>
            OnPosition, 

            /// <summary>
            /// Новое выделение в рамках текущего комбо
            /// </summary>
            NewHit,

            /// <summary>
            /// Со времени последнего выделения в старом комбо прошло слишком много времени. Это новое комбо.
            /// </summary>
            NewCombo
        }

        private HitTestResult CheckComboInterval(int currentID)
        {
            var finishTime = DateTime.Now;
            var betwen = finishTime.Subtract(lastHit);
            if(betwen.TotalMilliseconds > interval)
            {
                return HitTestResult.NewCombo;
            }
            if (/*betwen.TotalMilliseconds < stayInterval && */
                currentCombo.Count > 0 && 
                currentID == currentCombo.Last())
            {
                return HitTestResult.OnPosition;
            }
            else
            {
                return HitTestResult.NewHit;
            }
        }

        //private Color SelectControlPointColor(Bitmap bmp)
        //{
        //    int r = 0, g = 0, b = 0;
        //    int colorRadius = 7;
        //    for (int i = -3; i <= 3; i++)
        //    {
        //        for (int j = -3; j < 3; j++)
        //        {
        //            Color color = bmp.GetPixel(Width / 2 + i, Height / 2 + j);
        //            r += color.R;
        //            g += color.G;
        //            b += color.B;
        //        }
        //    }

        //    r = (int)((double)r / (colorRadius * colorRadius));
        //    g = (int)((double)g / (colorRadius * colorRadius));
        //    b = (int)((double)b / (colorRadius * colorRadius));
        //    Color middleColor = Color.FromArgb(r, g, b);
        //    return middleColor;
        //}
    }
}
